﻿using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Text;
using System.Web;

namespace BigfootWeb
{

    /// <summary>
    /// This class helps you deal with the strongly typed collection of values from the request.
    /// It also allows for you to pass in manual values, which is useful during testing. When instantiated
    /// with the empty constructor, it assumes that you are refering to the HttpContext.Current context
    /// </summary>
    public class PostHelper
    {
        private readonly HttpRequest Request;
        public NameValueCollection ManualValues = new NameValueCollection();
        public bool InTestingMode;

        /// <summary>
        /// Creates an instance of the class using the current context's HttpRequest
        /// </summary>
        public PostHelper()
        {
            this.Request = HttpContext.Current.Request;
        }

        /// <summary>
        /// Creates an instance of the class using the HttpRequest specified
        /// </summary>
        public PostHelper(HttpRequest request)
        {
            this.Request = request;
            InTestingMode = request == null ? true : false;
        }

        /// <summary>
        /// Creates an instance of the class using the HttpRequest specified. It also allows you to specify 
        /// manual values as well as determine weather you are in testing mode or not. If in testing mode
        /// the Request object will not be accessed, so you can create it with a null object
        /// </summary>
        public PostHelper(HttpRequest request, NameValueCollection manualValues, bool inTestingMode)
        {
            this.Request = request;
            this.ManualValues = manualValues;
            this.InTestingMode = request == null || inTestingMode;
        }
        
        /// <summary>
        /// Gets a value from the request. First it tries the QueryString and second it tries the form collection
        /// </summary>
        /// <param name="key">The parameter to get</param>
        /// <returns>The value of the parameter</returns>
        public string RequestValue(string key)
        {

            // Try to find the value in the test values
            if (ManualValues != null && !string.IsNullOrEmpty(ManualValues[key]))
                return ManualValues[key];

            // When in testing do not inspect the context
            if (InTestingMode) return null;

            if (!string.IsNullOrEmpty(Request.QueryString[key]))
                return Request.QueryString[key];

            if (!string.IsNullOrEmpty(Request.Form[key]))
                return Request.Form[key];

            return null;
        }

        /// <summary>
        /// Requires that a certain parameter be present in the request
        /// </summary>
        /// <param name="name">paramater name</param>
        /// <returns>The parameter value</returns>
        public string GetValueOrError(string name)
        {
            return GetValueOrError(name, false);
        }

        /// <summary>
        /// Requires that a certain parameter be present in the request
        /// </summary>
        /// <param name="name">paramater name</param>
        /// <returns>Convers the parameter value to an int</returns>
        public int GetValueOrErrorAsInt(string name)
        {
            RequireValue(name);
            return GetInt(name).Value;
        }

        /// <summary>
        /// Requires that a certain parameter be present in the request
        /// </summary>
        /// <param name="name">paramater name</param>
        /// <returns>Convers the parameter value to a double</returns>
        public double GetValueOrErrorAsDouble(string name)
        {
            RequireValue(name);
            return GetDouble(name).Value;
        }

        /// <summary>
        /// Requires that a certain parameter be present in the request
        /// </summary>
        /// <param name="name">paramater name</param>
        /// <returns>Convers the parameter value to a date</returns>
        public DateTime GetValueOrErrorAsDate(string name)
        {
            RequireValue(name);
            return GetDate(name).Value;
        }

        /// <summary>
        /// Requires that a certain parameter be present in the request
        /// </summary>
        /// <param name="name">paramater name</param>
        /// <param name="decode">indicates weather you want to UrlDecode the parameter</param>
        /// <returns>The parameter value</returns>
        public string GetValueOrError(string name, bool decode)
        {
            string value;
            if (string.IsNullOrEmpty(RequestValue(name)))
                throw new ApplicationException("Expected value not found: " + name);
            value = RequestValue(name);
            if (decode) value = HttpUtility.UrlDecode(value);
            return value;
        }

        /// <summary>
        /// Validates that a certain parameter be present is present in the request. Throws an error otherwise
        /// </summary>
        /// <param name="name">name of the parameter</param>
        public void RequireValue(string name)
        {
            if (string.IsNullOrEmpty(RequestValue(name)))
                throw new ApplicationException("Expected value not found: " + name);
        }

        /// <summary>
        /// Returns the value for a certain parameter (QueryString or Form)
        /// </summary>
        /// <param name="key">parameter name</param>
        /// <returns>value</returns>
        public string GetValue(string key)
        {
            return RequestValue(key);
        }

        /// <summary>
        /// Returns a value for a certain parameter typed as an integer
        /// </summary>
        public int? GetInt(string key)
        {
            var result = new int?();
            if (RequestValue(key) != null)
                result = int.Parse(GetValue(key));
            return result;
        }

        /// <summary>
        /// Returns a value for a certain parameter typed as a Decimal
        /// </summary>
        public decimal? GetDecimal(string key)
        {
            var result = new decimal?();
            if (RequestValue(key) != null)
                result = decimal.Parse(GetValue(key));
            return result;
        }

        /// <summary>
        /// Returns a value for a certain parameter typed as an double
        /// </summary>
        public double? GetDouble(string key)
        {
            var result = new double?();
            if (RequestValue(key) != null)
                result = double.Parse(GetValue(key));
            return result;
        }

        /// <summary>
        /// Returns a value for a certain parameter typed as an date 
        /// </summary>
        public DateTime? GetDate(string key)
        {
            var result = new DateTime?();
            DateTime value;
            if (RequestValue(key) != null && DateTime.TryParse(GetValue(key), out value))
            {
                result = value;
            }
            return result;
        }

        /// <summary>
        /// Returns a value for a certain parameter typed as a bool. Interprets: true | yes | on | 1   AS True
        /// </summary>
        public bool GetBool(string key)
        {
            return GetValue(key) != null &&
                        (GetValue(key).ToLower() == "true" ||
                         GetValue(key).ToLower() == "yes" ||
                         GetValue(key).ToLower() == "on" ||
                         GetValue(key).ToLower() == "1");
        }

        /// <summary>
        /// Returns a list of integers from 
        /// </summary>
        /// <param name="data">The string of ints i.e. "1,2,3,4"</param>
        /// <returns></returns>
        public List<int> GetIntArray(string data)
        {
            var result = new List<int>();
            if (RequestValue(data) != null)
            {
                var value = RequestValue(data);
                // Identify the devider
                var devider = ',';
                if (value.Contains(";")) { devider = ';'; }
                else if (value.Contains("|")) { devider = '|'; }
                // Strip the first and last characters if they are deviders
                if (value.StartsWith(devider.ToString())) value = value.Substring(1);
                if (value.EndsWith(devider.ToString())) value = value.Substring(0, value.Length - 2);

                // Split the string and add to the list
                foreach (var v in value.Split(devider))
                {
                    result.Add(int.Parse(v));
                }
            }
            return result;
        }

    }
}
